﻿using System;
using System.Collections.Generic;
using System.Data.Linq.SqlClient;
using System.Linq;
using System.Web.Mvc;
using SWS = System.Web.Security;
using PagedList;
using CodeCamp.Infrastructure;
using CodeCamp.Model;
using CodeCamp.Models.Sessions;
using CodeCamp.Models.Shared;
using RGoodSW.Logger.Interfaces;
using RGoodSW.Logger.MvcHelper.Models;

namespace CodeCamp.Controllers
{
    public class SessionsController : ControllerBase
  {
      private static class Tags
      {
          internal const string defaultLocalTimeConversionHours = "defaultSessionLocalTimeConversionHours";
          internal const string defaultLocalTimeZone = "defaultSessionLocalTimeZone";
          internal const string savedFilterModel = "siteSessionSavedFilterModel";
      }

      public SessionsController(IDefaultValueProvider defaultValueProvider, ILoggerExtended logger)
      : base(defaultValueProvider, logger)
    {
    }

    internal static string FilterDevSatTag( string sessionName )
    { return sessionName.StartsWith( "#devsat" ) ? sessionName.Substring( 8 ) : sessionName ; }

    #region List action

    // GET: /Sessions/List

    public ActionResult List( string eventName )
    {
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          var siteUser = GetSiteUser(repository, this.User, true);
          SetSharedViewBagProperties(ccEvent);
          int eventId = ccEvent.Id;

          var eventSessions =
            repository.GetPublicSessions(eventId)
            .Where(s => !s.Title.StartsWith("#devsat"))
            .OrderBy(s => s.Title);

          var userPresenter = GetPresenter(ccEvent.Id, siteUser);

          // assemble model

          var model = new SessionsListViewModel
          {
              EventTagsInUse = GetEventTagsInUse(ccEvent, repository),
              Sessions = new List<SessionViewModel>(),

              IsUserAuthenticated = User.Identity.IsAuthenticated,
              IsUserAPresenter = userPresenter != null,
              UserPresenterId = userPresenter != null
                ? userPresenter.Id
                : 0
          };
          SetSharedEventProperties(ccEvent, model, repository);

          foreach (var session in eventSessions)
          {
              var sessionTags = new SessionTags
              {
                  Tags =
                  (
                    from st in session.SessionTags
                    orderby st.Tag.Name
                    select new TagViewModel
                    {
                        TagId = st.TagId,
                        Name = st.Tag.Name
                    }
                  ).ToList()
              };

              var viewModel =
                new SessionViewModel
                {
                    Id = session.Id,
                    CreatedUTC = session.CreatedUTC,
                    Title = SessionTitle(session),
                    Description = session.Description,
                    Status = session.Status,
                    SessionPresenters = GetSessionPresenters(session),
                    SessionMaterialsUrl = session.SessionMaterialsUrl,
                    Tags = sessionTags,
                    ScheduleInfo = GetSessionScheduleInfo(repository, session.Id)
                };
              model.Sessions.Add(viewModel);
          }

          return View("List", model);
      }
    }

    #endregion

    #region Unschedued Session List (Admin)

    const string SessionPreviewRoles = CCRoles.RoleNames.EventContentEditor + ", " + CCRoles.RoleNames.EventTrackLead + ", " + CCRoles.RoleNames.BigCheeze;
    [CustomAuthorize(Roles = SessionPreviewRoles)]
    public ActionResult UnscheduledSessionGrid(string eventName)
    {
        using (var repository = new CodeCampDataRepository())
        {
            var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
            var siteUser = GetSiteUser(repository, this.User, true);
            SetSharedViewBagProperties(ccEvent);
            int eventId = ccEvent.Id;

            var eventSessions = repository.GetUnscheduledSessions(ccEvent.Id)
              .OrderBy(s => s.Title);

            var userPresenter = GetPresenter(ccEvent.Id, siteUser);

            // assemble model

            var model = new SessionsListViewModel
            {
                EventTagsInUse = GetEventTagsInUse(ccEvent, repository),
                Sessions = new List<SessionViewModel>(),

                IsUserAuthenticated = User.Identity.IsAuthenticated,
                IsUserAPresenter = userPresenter != null,
                UserPresenterId = userPresenter != null
                  ? userPresenter.Id
                  : 0
            };
            SetSharedEventProperties(ccEvent, model, repository);

            foreach (var session in eventSessions)
            {
                var sessionTags = new SessionTags
                {
                    Tags =
                    (
                      from st in session.SessionTags
                      orderby st.Tag.Name
                      select new TagViewModel
                      {
                          TagId = st.TagId,
                          Name = st.Tag.Name
                      }
                    ).ToList()
                };

                var viewModel =
                  new SessionViewModel
                  {
                      Id = session.Id,
                      CreatedUTC = session.CreatedUTC,
                      Title = session.Title,
                      Description = session.Description,
                      Status = session.Status,
                      SessionPresenters = GetSessionPresenters(session),
                      SessionMaterialsUrl = session.SessionMaterialsUrl,
                      Tags = sessionTags,
                      ScheduleInfo = GetSessionScheduleInfo(repository, session.Id)
                  };
                model.Sessions.Add(viewModel);
            }

            return View("UnscheduledSessionGrid", model);
        }
    }

#if true
    private IQueryable<Session> GetInitialQueryResult(CodeCampDataRepository ccdr,
        ISessionSearchFiler filter, TimeSpan localTimeConversion)
    {
        if (filter == null)
            throw new ArgumentNullException("filter");

        IQueryable<Session> initialQueryResult = null;

        initialQueryResult = ccdr.GetUnscheduledSessions(filter.EventId)
              .OrderBy(s => s.Title);

        return initialQueryResult;
    }

    private List<SessionListViewModel> GetSessionQueryList(CodeCampDataRepository ccdr, IQueryable<Session> initialQueryResult, ISessionSearchFiler filter)
    {
        List<SessionListViewModel> viewList = new List<SessionListViewModel>();

        foreach (Session session in initialQueryResult.ToList())
        {
            SessionListViewModel viewModel = new SessionListViewModel
            {
                Id = session.Id,
                CreatedUTC = session.CreatedUTC,
                Title = session.Title,
                Description = session.Description,
                DesiredRoomSize = session.DesiredRoomSize,
                SessionStatusId = session.SessionStatusId//,
                //Presenters = (
                //    from sp in session.SessionPresenters
                //    orderby sp.Presenter.LastName
                //    select new SessionPresenters.Presenter
                //    {
                //        Id = sp.PresenterId,
                //        Name = string.Format("{0} {1}", sp.Presenter.FirstName, sp.Presenter.LastName)
                //    }
                //).ToList(),
                //SessionTags = session.SessionTags
                //Tags = 
                //(
                //    from st in session.SessionTags
                //    orderby st.Tag.Name
                //    select new TagViewModel
                //    {
                //        TagId = st.TagId,
                //        Name = st.Tag.Name
                //    }
                //).ToList()
            };
            List<SessionPresenter> sessionPresenters = ccdr.EFContext.SessionPresenters
                                                            .Include("Presenter")
                                                            .Where(sp => sp.SessionId == session.Id)
                                                            .ToList();
            foreach (SessionPresenter sp in sessionPresenters)
            {
                SessionPresenters.Presenter pr = new SessionPresenters.Presenter
                {
                    Id = sp.PresenterId,
                    Name = string.Format("{0} {1}", sp.Presenter.FirstName, sp.Presenter.LastName)
                };
                viewModel.Presenters.Add(pr);
            }

            List<Tag> sessionTags = ccdr.EFContext.SessionTags
                                        .Where(st => st.SessionId == viewModel.Id)
                                        .Select(st => st.Tag)
                                        .ToList();

            System.Text.StringBuilder sbTags = new System.Text.StringBuilder();
            foreach (Tag tag in sessionTags)
            {
                if (sbTags.Length > 0)
                    sbTags.Append(", ");
                sbTags.Append(tag.Name);
            }
            viewModel.TagsCSV = sbTags.ToString();

            if (!string.IsNullOrWhiteSpace(filter.TagPart))
            {
                if (viewModel.TagsCSV.ToLower().IndexOf(filter.TagPart.ToLower()) >= 0)
                    viewList.Add(viewModel);
            }
            else
            {
                viewList.Add(viewModel);
            }
        }
        return viewList;
    }

#else
    private IQueryable<SessionListViewModel> GetProjectedSessions(CodeCampDataRepository ccdr,
        ISessionSearchFiler filter, TimeSpan localTimeConversion)
    {
        if (filter == null)
            throw new ArgumentNullException("filter");

        IQueryable<Session> initialQueryResult = null;

        initialQueryResult = ccdr.GetUnscheduledSessions(filter.EventId)
              .OrderBy(s => s.Title);

        var projectedSessions 
            = from session in initialQueryResult
            select new SessionListViewModel
            {
                Id = session.Id,
                CreatedUTC = session.CreatedUTC,
                Title = session.Title,
                Description = session.Description,
                DesiredRoomSize = session.DesiredRoomSize,
                SessionStatusId = session.SessionStatusId//,
                //Presenters = (
                //    from sp in session.SessionPresenters
                //    orderby sp.Presenter.LastName
                //    select new SessionPresenters.Presenter
                //    {
                //        Id = sp.PresenterId,
                //        Name = string.Format("{0} {1}", sp.Presenter.FirstName, sp.Presenter.LastName)
                //    }
                //).ToList(),
                //SessionTags = session.SessionTags
                //Tags = 
                //(
                //    from st in session.SessionTags
                //    orderby st.Tag.Name
                //    select new TagViewModel
                //    {
                //        TagId = st.TagId,
                //        Name = st.Tag.Name
                //    }
                //).ToList()
            };
        return projectedSessions;
    }
#endif

    //const string SessionPreviewRoles = CCRoles.RoleNames.EventContentEditor + ", " + CCRoles.RoleNames.EventTrackLead + ", " + CCRoles.RoleNames.BigCheeze;
    [CustomAuthorize(Roles = SessionPreviewRoles)]
    public ActionResult UnscheduledSessions(SessionSearchViewModel model, string eventName)
    {
        SessionsViewModel viewModel = UnscheduledSessions(model, eventName, false);
        return View(viewModel);
    }

    [CustomAuthorize(Roles = SessionPreviewRoles)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult UnscheduledSessions(SessionSearchViewModel model, string eventName, string TitleStart)
    {
        SessionsViewModel viewModel = UnscheduledSessions(model, eventName, true);
        return View(viewModel);
    }

    private SessionsViewModel UnscheduledSessions(SessionSearchViewModel model, string eventName, bool postback)
    {
        SetLoggerThreadContextData();
        using (var repository = new CodeCampDataRepository())
        {
            var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
            var siteUser = GetSiteUser(repository, this.User, true);
            SetSharedViewBagProperties(ccEvent);
            SessionSearchViewModel savedModel = null;
            SessionSearchViewModel queryModel = null;

            if (TempData.ContainsKey(Tags.savedFilterModel))
            {
                savedModel = TempData[Tags.savedFilterModel] as SessionSearchViewModel;
            }

            SessionsViewModel viewModel = new SessionsViewModel();

            SetSharedEventProperties(ccEvent, viewModel, repository);

            const int defaultSessionEntriesPerPage = 40;
            TimeSpan localTimeConversion = new TimeSpan(0); // LocalTimeConversion;

            if (postback)
            {
                if (!string.IsNullOrWhiteSpace(model.ClearButton))
                {
                    savedModel = null;
                    model = new SessionSearchViewModel
                    {
                        Page = 1,
                        RowsPerPage = defaultSessionEntriesPerPage
                    };
                    ModelState.Clear();
                }
                else
                {
                    if (ModelState.IsValid)
                    {
                        //New search
                        model.Page = 1;
                        queryModel = model;
                        if (model.UTCAdjustMinutes.HasValue)
                            localTimeConversion = new TimeSpan(0, model.UTCAdjustMinutes.Value, 0);
                    }
                    else
                    {
                        if (savedModel != null)
                        {
                            queryModel = savedModel;
                        }
                    }
                }
            }
            else
            {
                Nullable<int> page = null;
                if (savedModel != null)
                {
                    if (!page.HasValue)
                        page = model.Page;
                    model = savedModel;
                    model.Page = page;
                    if (model.UTCAdjustMinutes.HasValue)
                        localTimeConversion = new TimeSpan(0, model.UTCAdjustMinutes.Value, 0);
                }
            }

            if (queryModel == null)
                queryModel = model;

            //if (ModelState.ContainsKey("FilterTimeTicks"))
            //    ModelState.Remove("FilterTimeTicks");

            viewModel.SearchResultsViewModel = model;
            queryModel.EventId = ccEvent.Id;
            if (!queryModel.RowsPerPage.HasValue)
                queryModel.RowsPerPage = defaultSessionEntriesPerPage;

            if (postback || (savedModel != null))
            {
                var pageIndex = model.Page ?? 1;
                Logger.DebugFormat("Sessions LocalTimeConversion.Hours={0}", localTimeConversion.Hours);

#if true
                IQueryable<Session> initialQueryResult = GetInitialQueryResult(repository, queryModel, localTimeConversion);
                List<SessionListViewModel> viewList = GetSessionQueryList(repository, initialQueryResult, queryModel);
                var sessionsAsIPagedList = new StaticPagedList<SessionListViewModel>(viewList, pageIndex, queryModel.RowsPerPage.Value, viewList.Count);
                viewModel.SearchResultsViewModel.SearchResults = sessionsAsIPagedList;
#else

                var projectedSessions = GetProjectedSessions(repository, queryModel, localTimeConversion);
                viewModel.SearchResultsViewModel.SearchResults = projectedSessions.ToPagedList<SessionListViewModel>(pageIndex, queryModel.RowsPerPage.Value);
#endif
            }
            //model.FilterTime = DateT ime.Now;
            if (TempData.ContainsKey(Tags.savedFilterModel))
            {
                TempData[Tags.savedFilterModel] = model;
            }
            else
            {
                TempData.Add(Tags.savedFilterModel, model);
            }

            return viewModel;
        }
    }

    #endregion

    #region Details action

    // GET: /Sessions/Details/5

    public ActionResult Details( int id, string eventName )
    {
      SetLoggerThreadContextData();
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          SetSharedViewBagProperties(ccEvent);
          int eventId = ccEvent.Id;
          var session = repository.GetSession(ccEvent.Id, id);

          if (session == null)
          {
              Logger.WarnFormat("Session {0} for event {1} not found.", id, ccEvent.Id);
              //return new HttpNotFoundResult("Session not found");
              return RedirectToAction("List");
          }

          var model = new SessionsDetailsViewModel
          {
              SessionId = id,
              Title = SessionTitle(session), //FilterDevSatTag(session.Title),
              StatusID = session.SessionStatusId,
              Description = session.Description,
              SessionPresenters = GetSessionPresenters(session),
              SessionTags = GetSessionTags(session),
              AllEventTags = GetAllEventTags(ccEvent.Id, repository),
              EventTagsInUse = GetEventTagsInUse(ccEvent, repository),
              SessionMaterialsUrl = session.SessionMaterialsUrl,
              SessionScheduleInfo = GetSessionScheduleInfo(repository, session.Id),
              IsCurrentUserAuthorizedToEdit = IsCurrentUserAuthorizedToEditSession(session)
          };

          bool sessionIsPublic = SessionIsPublic(session);
          if (!sessionIsPublic)
          {
              if (!IsCurrentUserAuthorizedToEditSession(session) && !User.IsInRole(CCRoles.RoleNames.EventTrackLead))
              {
                  return RedirectToAction("List");
              }
              model.Title = string.Format("{0} - {1}", model.Status, model.Title);
          }
          if (User.IsInRole(CCRoles.RoleNames.BigCheeze))
          {
              if (!string.IsNullOrWhiteSpace(session.OrganizerNotes) || !string.IsNullOrWhiteSpace(session.PrivateNotes))
              {
                  model.PrivateInfo = new SessionsDetailsViewModel.SessionsDetailPrivateInformation();
                  model.PrivateInfo.OrganizerNotes = session.OrganizerNotes;
                  model.PrivateInfo.PrivateNotes = session.PrivateNotes;
              }
          }
          SetSharedEventProperties(ccEvent, model, repository);
          model.DefaultEmailFromAddress = DefaultEmailFromAddress;
          model.DefaultPresenterReplyEmailAddress = DefaultPresenterReplyEmailAddress;

          return View("Details", model);
      }
    }

    const string SessionEditRoles = CCRoles.RoleNames.Presenter + ", " + CCRoles.RoleNames.EventContentEditor + ", " + CCRoles.RoleNames.BigCheeze;
    [CustomAuthorize(Roles = SessionEditRoles)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult DetailsSetTags(SessionsDetailsViewModel model, string eventName, int[] sessionTags)
    {
        SetLoggerThreadContextData();
        using (var repository = new CodeCampDataRepository())
        {
            var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
            SetSharedViewBagProperties(ccEvent);
            Session session = repository.GetSession(ccEvent.Id, model.SessionId);
            if (session != null)
            {
                if (!SetSessionTags(repository, session, sessionTags, "Sessions/DetailsSetTags POST"))
                {
                    //If the problem shows up again, may want to report back to UI
                }
            }
            return RedirectToAction("Details", new { id = model.SessionId });
        }
    }

    [CustomAuthorize(Roles = SessionEditRoles)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult DetailsAddTag( SessionsDetailsViewModel model, string eventName, string newTagName )
    {
      //SetLoggerThreadContextData();
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          SetSharedViewBagProperties(ccEvent);
          CreateTagAndAddToSession(repository, ccEvent.Id, model.SessionId, newTagName);
          return RedirectToAction("Details", new { id = model.SessionId });
      }
    }
    #endregion

    #region Add action

    private void LogPresenterNotAuthorized(string action, Presenter presenter, Nullable<int> id)
    {
        Logger.WarnFormat(new LogContextData(this.User, this.Request),
            "User not authorized for \"{1}\" on id {2}. {0}id of presenter found={3}, presenter.SiteUserId={4}.  ",
            Environment.NewLine,
            action,
            id,
            presenter == null ? -1 : presenter.Id,
            presenter == null ? -1 : presenter.SiteUserId
            );
    }

    private void LogPresenterNotAuthorized(string action, Session session, int id)
    {
        Logger.WarnFormat(new LogContextData(this.User, this.Request),
            "User not authorized for \"{1}\" on id {2}. {0}id of session found={3}",
            Environment.NewLine,
            action,
            id,
            session == null ? -1 : session.Id
            );
    }

    // GET: /Sessions/Add
    [CustomAuthorize(Roles = CCRoles.RoleNames.Presenter)]
    public ActionResult Add(string eventName)
    {
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          var siteUser = GetSiteUser(repository, this.User, true);
          SetSharedViewBagProperties(ccEvent);
          int eventId = ccEvent.Id;

          var model = new SessionsEditViewModel();
          SetSharedEventProperties(ccEvent, model, repository);
          if (model.SubmissionsOpen)
          {
              var presenter = GetPresenter(ccEvent.Id, siteUser);
              if (presenter == null)
              {
                  Nullable<int> id = null;
                  LogPresenterNotAuthorized("Add", presenter, id);
                  ErrorViewModel evModel = new ErrorViewModel();
                  SetSharedEventProperties(ccEvent, evModel, repository);
                  return View("AuthorizationError", evModel);
              }

              model.PresenterId = presenter.Id;
              model.HideNotes = true;
              return View("Add", model);
          }
          else
          {
              var scModel = new SessionsAddClosedViewModel();
              SetSharedEventProperties(ccEvent, scModel, repository);
              return View("AddClosed", scModel);
          }
      }
    }

    // POST: /Sessions/Add

    [AcceptVerbs( HttpVerbs.Post )]
    [CustomAuthorize(Roles = CCRoles.RoleNames.Presenter)]
    public ActionResult Add(SessionsEditViewModel model, string eventName)
    {
      try
      {
          using (var repository = new CodeCampDataRepository())
          {
              var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
              var siteUser = GetSiteUser(repository, this.User, true);
              SetSharedViewBagProperties(ccEvent);
              SetSharedEventProperties(ccEvent, model, repository);
              if (!model.SubmissionsOpen)
              {
                  var scModel = new SessionsAddClosedViewModel();
                  SetSharedEventProperties(ccEvent, scModel, repository);
                  return View("AddClosed", scModel);
              }

              var presenter = GetPresenter(ccEvent.Id, siteUser);
              if ((presenter == null) || (presenter.Id != model.PresenterId))
              {
                  Nullable<int> id = null;
                  LogPresenterNotAuthorized("Add", presenter, id);
                  ErrorViewModel evModel = new ErrorViewModel();
                  SetSharedEventProperties(ccEvent, evModel, repository);
                  return View("AuthorizationError", evModel);
              }

              if (ModelState.IsValid)
              {
                  // add session
                  var session = new Session
                  {
                      Event = ccEvent,
                      EventId = ccEvent.Id,
                      Title = model.Title,
                      Description = model.Description,
                      SessionMaterialsUrl = model.SessionMaterialsUrl,
                      DesiredRoomSize = model.DesiredRoomSize,
                      Status = Model.Session.SessionStatus.Submitted
                  };

                  repository.Add(session);
                  repository.Save();

                  // add session presenter

                  var sessionPresenter = new SessionPresenter
                  {
                      Presenter = presenter,
                      PresenterId = model.PresenterId,
                      Session = session,
                      SessionId = session.Id
                  };
                  if (!repository.SiteUserIsInRole(siteUser.Id, CCRoles.RoleNames.Presenter))
                      repository.AddSiteUserToRole(siteUser, LogName(siteUser), CCRoles.RoleNames.Presenter, true);
                  repository.Add(sessionPresenter);
                  repository.Save();

                  return RedirectToAction("Details", new { id = session.Id });
              }
              model.HideNotes = true;
              return View("Add", model);
          }
      }
      catch (Exception ex)
      {
        LogException("Sessions/Add POST", ex);
        return View( "Add", model );
      }
    }

    #endregion

    #region Edit action

    // GET: /Sessions/Edit/5
    [CustomAuthorize(Roles = SessionEditRoles)]
    public ActionResult Edit(int id, string eventName)
    {
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          SetSharedViewBagProperties(ccEvent);
          int eventId = ccEvent.Id;

          var session = repository.GetSession(ccEvent.Id, id);

          // verify that user is authorized to edit this session
          if (!IsCurrentUserAuthorizedToEditSession(session))
          {
              LogPresenterNotAuthorized("Edit", session, id);
              ErrorViewModel evm = new ErrorViewModel();
              SetSharedEventProperties(ccEvent, evm, repository);
              return View("SessionEditAuthorizationError", evm);
          }

          var sessionPresenter = session.SessionPresenters.SingleOrDefault();

          var model = new SessionsEditViewModel
          {
              HideNotes = false,
              SessionId = session.Id,
              Status = session.Status,
              Title = session.Title,
              Description = session.Description,
              SessionMaterialsUrl = session.SessionMaterialsUrl,
              PresenterId = sessionPresenter.PresenterId,
              DesiredRoomSize = session.DesiredRoomSize,
              PrivateNotes = session.PrivateNotes
          };
          if (User.IsInRole(CCRoles.RoleNames.BigCheeze) || User.IsInRole(CCRoles.RoleNames.EventContentEditor))
              model.OrganizerNotes = session.OrganizerNotes;

          SetSharedEventProperties(ccEvent, model, repository);

          return View("Edit", model);
      }
    }

    // POST: /Sessions/Edit/5

    [AcceptVerbs( HttpVerbs.Post )]
    [CustomAuthorize(Roles = SessionEditRoles)]
    public ActionResult Edit(SessionsEditViewModel model, string eventName, int id)
    {
      try
      {
          using (var repository = new CodeCampDataRepository())
          {
              var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
              SetSharedViewBagProperties(ccEvent);
              int eventId = ccEvent.Id;
              var session = repository.GetSession(ccEvent.Id, id);
              SetSharedEventProperties(ccEvent, model, repository);

              // verify that user is authorized to edit this session
              if (!IsCurrentUserAuthorizedToEditSession(session))
              {
                  LogPresenterNotAuthorized("Edit-Post", session, id);
                  ErrorViewModel evm = new ErrorViewModel();
                  SetSharedEventProperties(ccEvent, evm, repository);
                  return View("SessionEditAuthorizationError", evm);
              }

              if (ModelState.IsValid)
              {
                  SiteUser siteUser = GetSiteUser(repository, this.User);

                  session.Title = model.Title;
                  session.Description = model.Description;
                  session.SessionMaterialsUrl = model.SessionMaterialsUrl;
                  session.DesiredRoomSize = model.DesiredRoomSize;
                  session.UpdatedBy = UpdateUser(siteUser);
                  session.UpdatedUTC = DateTime.UtcNow;
                  if (!model.HideNotes)
                  {
                      session.PrivateNotes = model.PrivateNotes;
                      if (User.IsInRole(CCRoles.RoleNames.BigCheeze) || User.IsInRole(CCRoles.RoleNames.EventContentEditor))
                      {
                          session.OrganizerNotes = model.OrganizerNotes;
                          session.SessionStatusId = (int)model.Status;
                      }
                  }

                  //TO DO: revisit this when testing add
                  //var sessionPresenter = session.SessionPresenters.SingleOrDefault();
                  //sessionPresenter.PresenterId = model.PresenterId;

                  repository.Save();

                  return RedirectToAction("Details", new { id = model.SessionId });
              }
              model.HideNotes = false;
              return View("Edit", model);
          }
      }
      catch (System.Data.Entity.Infrastructure.DbUpdateException ex)
      {
          Logger.ErrorFormat(new LogContextData(this.User, this.Request), ex,
              "{2} saving session edit for session {0} by user \"{1}\": {3}", id, this.User.Identity.Name, ex.GetType(), ex.Message);
          ModelState.AddModelError("", "Error saving session edits.");
          return View("Edit", model);
      }
      catch (Exception ex)
      {
          Logger.ErrorFormat(new LogContextData(this.User, this.Request), ex,
              "{2} saving session edit for session {0} by user \"{1}\": {3}", id, this.User.Identity.Name, ex.GetType(), ex.Message);
          ModelState.AddModelError("", "Error saving session edits.");
          return View("Edit", model);
      }
    }

    #endregion

    #region Tag action

    public ActionResult Tag( int id, string eventName )
    {
      using (var repository = new CodeCampDataRepository())
      {
          var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
          SetSharedViewBagProperties(ccEvent);
          int eventId = ccEvent.Id;
#if true
          string tagName;
          var taggedSessions = repository.GetTaggedSessions(ccEvent.Id, id, out tagName);
          if (taggedSessions == null)
              return RedirectToAction("List");
#else
          var tag = repository.GetTag(id);
          var taggedSessions =
            from st in tag.SessionTags
            orderby st.Session.Title
            select st.Session;
#endif

          var sessionsList = new List<SessionViewModel>();
          foreach (var taggedSession in taggedSessions)
          {
              sessionsList.Add(
                new SessionViewModel
                {
                    Id = taggedSession.Id,
                    Title = FilterDevSatTag(taggedSession.Title),
                    Description = taggedSession.Description,
                    SessionPresenters = GetSessionPresenters(taggedSession),
                    ScheduleInfo = GetSessionScheduleInfo(repository, taggedSession.Id),
                    Tags = GetSessionTags(taggedSession)
                });
          }

          var model = new SessionsTagViewModel
          {
              ThisTag = new TagViewModel { TagId = id, Name = tagName },
              Sessions = sessionsList,
              EventTagsInUse = GetEventTagsInUse(ccEvent, repository)
          };
          SetSharedEventProperties(ccEvent, model, repository);

          return View("Tag", model);
      }
    }

    #endregion

    #region Helpers

    private bool IsCurrentUserAuthorizedToEditSession( Session session )
    {
        if (User.IsInRole(CCRoles.RoleNames.EventContentEditor))
            return true;
        if (User.IsInRole(CCRoles.RoleNames.BigCheeze))
            return true;

#if USE_WIF
        Presenter sessionPresenterMatchingUser = GetSessionPresenterMatchingUser(this.User, session);
#endif

#if USE_MEMBERSHIP
      var sessionPresenterMatchingUser =
        ( 
          from sp in session.SessionPresenters
          //TODO: Revisit when doing claims auth
          where sp.Presenter.SiteUser != null
          where sp.Presenter.SiteUser.UserIdentityClaim == User.Identity.Name
          select sp.Presenter
        ).FirstOrDefault();
#endif

      return sessionPresenterMatchingUser != null;
    }


    private List<EventTags.EventTag> GetAllEventTags(int eventId, CodeCampDataRepository repository)
    {
        var eventTags =
            (
              from t in repository.GetTags(eventId)
              select new EventTags.EventTag
              {
                  Id = t.Id,
                  Name = t.Name,
                  Count = t.SessionTags.Count()
              }
            ).ToList();
        return eventTags;
    }

    private EventTags GetEventTagsInUse( Event ccEvent, CodeCampDataRepository repository )
    {
      var eventTags = new EventTags(GetAllEventTags(ccEvent.Id, repository));

      //Remove tags with 0 entries
      List<EventTags.EventTag> zeroList = new List<EventTags.EventTag>();
      foreach (var eventTag in eventTags.Tags)
      {
          if (eventTag.Count == 0)
              zeroList.Add(eventTag);
      }
      foreach (var eventTag in zeroList)
      {
          eventTags.Tags.Remove(eventTag);
      }
      SetSharedEventProperties(ccEvent, eventTags, repository);
      return eventTags;
    }

    private static SessionTags GetSessionTags( Session session )
    {
      return new SessionTags
      {
        Tags =
        (
          from st in session.SessionTags
          orderby st.Tag.Name
          select new TagViewModel
          {
            TagId = st.TagId,
            Name = st.Tag.Name
          }
        ).ToList()
      };
    }

    private static SessionPresenters GetSessionPresenters( Session session )
    {
      return new SessionPresenters
      {
        Presenters =
        (
          from sp in session.SessionPresenters
          orderby sp.Presenter.LastName
          select new SessionPresenters.Presenter
          {
            Id = sp.PresenterId,
            Name = string.Format( "{0} {1}", sp.Presenter.FirstName, sp.Presenter.LastName )
          }
        ).ToList()
      };
    }

    private bool SetSessionTags(CodeCampDataRepository repository, Session session, int[] sessionTagIds,
        string controllerAndAction)
    {
        try
        {
            var currentSessionTags = repository.GetSessionTags(session.Id).ToList();

            if (sessionTagIds != null)
            {
                foreach (var tagId in sessionTagIds)
                {
                    int index = currentSessionTags.FindIndex(t => t.TagId == tagId);
                    if (index == -1)      // tagId not in current session tags
                    {
                        SessionTag sessionTag = new SessionTag { SessionId = session.Id, TagId = tagId };
                        sessionTag.Tag = repository.GetTag(tagId);
                        session.SessionTags.Add(sessionTag);
                        repository.Add(sessionTag);
                    }
                    else                    // tagId in current session tags
                    {
                        currentSessionTags.RemoveAt(index);
                    }
                }
            }
            foreach (var currentSessionTag in currentSessionTags)
            {
                session.SessionTags.Remove(currentSessionTag);
                repository.Delete(currentSessionTag);
            }

            repository.Save();
            return true;
        }
        catch (System.Data.Entity.Validation.DbEntityValidationException ex)
        {
            LogException(controllerAndAction, ex);
        }
        catch (System.Data.Entity.Infrastructure.DbUpdateException ex)
        {
            Logger.ErrorFormat(new LogContextData(this.User, this.Request), ex,
                "{2} updating tags for session {0} by user \"{1}\": {3}", session.Id, this.User.Identity.Name, ex.GetType(), ex.Message);
        }
        catch (Exception ex)
        {
            Logger.ErrorFormat(new LogContextData(this.User, this.Request), ex,
                "{2} updating tags for session {0} by user \"{1}\": {3}", session.Id, this.User.Identity.Name, ex.GetType(), ex.Message);
        }
        return false;
    }

    private void CreateTagAndAddToSession( CodeCampDataRepository repository, int eventId, int sessionId, string newTagName )
    {
        if (!string.IsNullOrEmpty(newTagName)) // valid tag name
        {
            newTagName = newTagName.Trim();

            if (!string.IsNullOrEmpty(newTagName)) // tag not all whitespace
            {
                Session session = repository.GetSession(eventId, sessionId);
                if (session != null)
                {
                    SessionTag sessionTag = session.SessionTags.Where(st => st.Tag.Name.Equals(newTagName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
                    if (sessionTag == null)
                    {
                        CodeCamp.Model.Tag tag = repository.GetTag(eventId, newTagName);
                        if (tag == null) // tag doesn't exist
                        {
                            // create tag
                            tag = new CodeCamp.Model.Tag { EventId = eventId, Name = newTagName };
                            repository.Add(tag);
                            repository.Save();
                        }
                        AddTagToSession(repository, session, eventId, tag.Id);
                    }
                }
            }
        }
    }

    private void AddTagToSession( CodeCampDataRepository repository, Session session, int eventId, int tagId  )
    {
        SessionTag sessionTag = new SessionTag { SessionId = session.Id, TagId = tagId };
        sessionTag.Session = session;
        session.SessionTags.Add(sessionTag);
        repository.Save();
    }


    private static SessionScheduleInfo GetSessionScheduleInfo( CodeCampDataRepository repository, int sessionId )
    {
      SessionScheduleInfo sessionScheduleInfo = null;
      var scheduledSession = repository.GetScheduledSession( sessionId );
      if ( scheduledSession != null )
      {
        var room = scheduledSession.Room;
        var timeslot = scheduledSession.Timeslot;

        sessionScheduleInfo = new SessionScheduleInfo
        {
          Building = room.Building.Name,
          Room = room.Name,
          RoomId = room.Id,
          Date = timeslot.Date,
          StartTime = DateTimeHelper.CombineDateAndTime( timeslot.Date, timeslot.StartTime ),
          EndTime = DateTimeHelper.CombineDateAndTime( timeslot.Date, timeslot.EndTime )
        };
      }
      return sessionScheduleInfo;
    }

    #endregion
  }
}
